package com.sunop.userservice.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.OSSObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunop.panticommon.common.properties.OssProperties;
import com.sunop.panticommon.entity.Result;
import com.sunop.userservice.mapper.UserMapper;
import com.sunop.userservice.model.*;
import com.sunop.userservice.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @since 2025-03-12
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private static final String VERIFICATION_CODE_PREFIX = "verification:code:";
    private static final String RATE_LIMIT_PREFIX = "email:rate:limit:";
    private static final long CODE_EXPIRE_MINUTES = 5; // 验证码过期时间（分钟）
    private static final long RATE_LIMIT_MINUTES = 3; // 限流时间（分钟）

    private final UserMapper userMapper;
    private final OSS ossClient;
    private final OssProperties ossProperties;
    private final RedisTemplate redisTemplate;
    private final RabbitTemplate rabbitTemplate;
    private final JavaMailSender mailSender;

    @Value("${aes.key}")
    String key;
    @Value("${spring.mail.username}")
    private String fromEmail;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result register(UserRegisterDto userRegisterDto) {
        if (!verifyVerificationCode(userRegisterDto.getEmail(), userRegisterDto.getCode())) {
            return Result.error("验证码错误或已过期");
        }
        deleteVerificationCode(userRegisterDto.getEmail());
        // 加密
        userRegisterDto.setPassword(SaSecureUtil.aesEncrypt(key, userRegisterDto.getPassword()));
        if (userMapper.insertuser(userRegisterDto) && userMapper.insertrole(userRegisterDto.getRole())) return Result.success(null, "注册成功");
        return Result.error("注册失败");
    }

    @Override
    public Integer selectByNameAndPassword(String username, String password) {
        return userMapper.selectByNameAndPassword(username, SaSecureUtil.aesEncrypt(key, password));
    }

    /**
     * 上传文件到OSS
     * @param file 文件
     * @return 文件URL
     */
    @Override
    public Result uploadFile(MultipartFile file) {
        // 1. 获取当前用户ID
        int userId = StpUtil.getLoginIdAsInt();
        String userIdStr = String.valueOf(userId);

        // 2. 检查用户是否已有头像，并获取旧图片路径
        String oldFilePath = userMapper.getFilePath(userId);

        // 3. 生成新文件名和存储路径
        String originalFilename = file.getOriginalFilename();
        String fileExtension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        String uuidFilename = UUID.randomUUID() + fileExtension;
        String newStoragePath = String.format("users/%s/%s", userIdStr, uuidFilename);

        try {
            // 4. 上传新文件到OSS
            ossClient.putObject(
                    ossProperties.getBucketName(),
                    newStoragePath,
                    new ByteArrayInputStream(file.getBytes())
            );

            // 5. 如果存在旧文件，则删除
            if (oldFilePath != null && !oldFilePath.isEmpty()) {
                try {
                    ossClient.deleteObject(ossProperties.getBucketName(), oldFilePath);
                } catch (Exception e) {
                    log.error("删除旧头像失败: {}", oldFilePath, e);
                    // 不中断流程，继续执行
                }
            }

            // 6. 更新数据库中的文件路径
            userMapper.updateAvatar(newStoragePath, userId);

            // 7. 生成访问URL
            String fileUrl = String.format("https://%s.%s/%s",
                    ossProperties.getBucketName(),
                    ossProperties.getEndpoint().replace("http://", ""),
                    newStoragePath);

            return Result.success(fileUrl, "头像更新成功");
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

    /**
     * 下载文件
     * @param path 文件路径
     * @return 文件流
     */
    @Override
    public InputStream downloadFile(String path) {
        OSSObject ossObject = ossClient.getObject(ossProperties.getBucketName(), path);
        return ossObject.getObjectContent();
    }

    @Override
    public String getFilePath() {
        return userMapper.getFilePath(StpUtil.getLoginIdAsInt());
    }
    /*
      删除文件
      @param path 文件路径
     */
//    public void deleteFile(String path) {
//        ossClient.deleteObject(ossProperties.getBucketName(), path);
//    }

    // 验证码相关实现
    @Override
    public String generateVerificationCode() {
        Random random = new Random();
        return String.format("%06d", random.nextInt(999999));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result updateInfo(UserUpdateInfo userUpdateInfo) {
        try {
            userMapper.updateUserInfo(userUpdateInfo, StpUtil.getLoginIdAsInt());
            userMapper.updateRoleInfo(userUpdateInfo.getNumber(),StpUtil.getLoginIdAsInt());
            return Result.success(null, "更新成功");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public UserBaseDTO getUserBaseDTO(Integer userId) {
        return userMapper.getUserBaseDTO(userId);
    }

    @Override
    public int getclassId(int userId) {
        return userMapper.getclassId(userId);
    }

    @Override
    public List<Integer> getUserOverProblemIds(int userId) {
        return userMapper.getUserOverProblemIds(userId);
    }

    @Override
    public void storeVerificationCode(String email, String code) {
        String key = VERIFICATION_CODE_PREFIX + email;
        redisTemplate.opsForValue().set(key, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
    }

    @Override
    public boolean verifyVerificationCode(String email, String code) {
        String storedCode = null;
        try {
            String key = VERIFICATION_CODE_PREFIX + email;
            storedCode = (redisTemplate.opsForValue().get(key)).toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return code.equals(storedCode);
    }

    @Override
    public void deleteVerificationCode(String email) {
        String key = VERIFICATION_CODE_PREFIX + email;
        redisTemplate.delete(key);
    }

    // 频率限制实现
    @Override
    public boolean isEmailRateLimited(String email) {
        String key = RATE_LIMIT_PREFIX + email;
        if (redisTemplate.opsForValue().get(key) != null) {
            return true;
        }
        redisTemplate.opsForValue().set(key, "1", RATE_LIMIT_MINUTES, TimeUnit.MINUTES);
        return false;
    }

    @Retryable(value = {MailException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    @Override
    public void sendVerificationEmail(String toEmail, String verificationCode) {
        SimpleMailMessage message = new SimpleMailMessage();
        // 使用配置中的邮箱作为发件人
        message.setFrom(fromEmail);
        message.setTo(toEmail);
        message.setSubject("判题服务|注册验证码");
        message.setText("这是练手的判题服务。  您的验证码是: " + verificationCode + "\n验证码5分钟内有效");

        mailSender.send(message);
    }

    @Recover
    public void recoverSendMail(MailException e, String toEmail, String verificationCode) {
        // 失败处理逻辑
        log.error("邮件发送失败，已重试3次: " + toEmail);
    }

    // 业务方法实现
    @Override
    public Result sendVerificationCode(String email) {
        if (isEmailRateLimited(email)) {
            return Result.error("操作过于频繁，请等待三分钟后再尝试");
        }

        String code = generateVerificationCode();
        storeVerificationCode(email, code);
        EmailMessage message = new EmailMessage(email, code);
        rabbitTemplate.convertAndSend("email.exchange", "email.routing.key", message);

        return Result.success("验证码已发送");
    }
}
